package com.web_bloks.mvc.client;

import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashMap;

import com.google.gwt.core.client.GWT;

/**
 * 
 * @author Jean-Lou Dupont
 *
 */
public class View implements IView {

	static View instance = null;
	
	// Mapping of Mediator names to Mediator instances
	// Mapping of Notification names to Observer lists
	private LinkedHashMap<String, IMediator> mediatorMap;
	private LinkedHashMap<String, Observers> observerMap;

	/**
	 * Constructor.
	 * 
	 * <P>
	 * This <code>IView</code> implementation is a Singleton, so you should
	 * not call the constructor directly, but instead call the static Singleton
	 * Factory method <code>View.getInstance()</code>
	 * 
	 * @throws Error
	 *              Error if Singleton instance has already been constructed
	 * 
	 */
	protected View( ) {
		super();
		this.mediatorMap = new LinkedHashMap<String, IMediator>();
		this.observerMap = new LinkedHashMap<String, Observers>();
		initializeView();
	}

	/**
	 * Initialize the Singleton View instance.
	 */
	protected void initializeView( ) {
	}

	/**
	 * View Singleton Factory method.
	 * 
	 * @return the Singleton instance of <code>View</code>
	 */
	public static View getInstance( ) {
		if (instance == null) {
			instance = (View) GWT.create( View.class );
		}
		return instance;
	}

	/**
	 * Notify the <code>Observers</code> for a particular
	 * <code>Notification</code>.
	 * 
	 * <P>
	 * All previously attached <code>Observers</code> for this
	 * <code>Notification</code>'s list are notified and are passed a
	 * reference to the <code>Notification</code> in the order in which they
	 * were registered.
	 * </P>
	 * 
	 * @param note
	 *             the <code>Notification</code> to notify
	 *             <code>Observers</code> of.
	 */
	public void notifyObservers( INotification note ) {
		
		Observers observers = (Observers) this.observerMap.get( note.getNotificationName() );
		if (observers != null) {
			observers.notifyObservers(note);
		}
	}

	/**
	 * Register an <code>Mediator</code> instance with the <code>View</code>.
	 * 
	 * @param mediator
	 */
	public void registerMediator( final IMediator mediator ) {
		
		// Register the Mediator for retrieval by name
		this.mediatorMap.put(mediator.getMediatorName(), mediator);

		// Get Notification interests, if any.
		String[] noteInterests = mediator.listNotificationInterests();
		if (noteInterests.length == 0) {
			return;
		}

		// Create java style function ref to mediator.handleNotification
		IMethod function = new IMethod() {

			public void onNotification( INotification notification ) {
				mediator.handleNotification(notification);
			}
		};

		// Create Observer
		//Observer observer = new Observer(function, mediator);
		Observer observer = (Observer) GWT.create( Observer.class );
		observer.init(function, mediator);

		// Register Mediator as Observer for its list of Notification
		// interests
		for (int i = 0; i < noteInterests.length; i++) {
			registerObserver(noteInterests[i], observer);
		}
	}

	/**
	 * Register an <code>Observer</code> to be notified of
	 * <code>INotifications</code> with a given name.
	 * 
	 * @param notificationName
	 *             the name of the <code>Notifications</code> to notify this
	 *             <code>Observer</code> of
	 * @param observer
	 *             the <code>Observer</code> to register
	 */
	public void registerObserver( String notificationName, IObserver observer ) {
		
		if (this.observerMap.get(notificationName) != null) {
			Observers observers = (Observers) this.observerMap.get(notificationName);
			observers.addObserver(observer);
		} else {
			this.observerMap.put(notificationName, new Observers(notificationName, observer));
		}
	}

	/**
	 * Remove an <code>Mediator</code> from the <code>View</code>.
	 * 
	 * @param mediatorName
	 *             name of the <code>Mediator</code> instance to be removed.
	 */
	public IMediator removeMediator( String mediatorName ) {
		
		// Remove all Observers with a reference to this Mediator
		// also, when an notification's observer list length falls to
		// zero, remove it.
		Observers temp = null;
		IObserver observer = null;

		Collection<Observers> c = this.observerMap.values();
		
		Iterator<Observers> iter = c.iterator();
		
		//for (Enumeration<Observers> enu = this.observerMap.elements(); enu.hasMoreElements();) {
		while(iter.hasNext()) {
			
			//temp = (Observers) enu.nextElement();
			temp = iter.next();
			
			for (Enumeration<IObserver> e = temp.getObservers().elements(); e.hasMoreElements();) {
				observer = (IObserver) e.nextElement();
				if (observer.compareNotifyContext(retrieveMediator(mediatorName))) {
					temp.deleteObserver(observer);
				}
				if (temp.getObservers().size() < 1) {
					this.observerMap.remove(temp.getNotification());
				}
			}
			
		}

		// Remove the reference to the Mediator itself
		return (IMediator) this.mediatorMap.remove(mediatorName);

	}

	/**
	 * Retrieve an <code>Mediator</code> from the <code>View</code>.
	 * 
	 * @param mediatorName
	 *             the name of the <code>Mediator</code> instance to
	 *             retrieve.
	 * @return the <code>Mediator</code> instance previously registered with
	 *         the given <code>mediatorName</code>.
	 */
	public IMediator retrieveMediator( String mediatorName ) {
		
		return (IMediator) this.mediatorMap.get(mediatorName);
	}

	/**
	 * Removes all Observers & Mediators 
	 */
	public void clear() {
		this.mediatorMap.clear();
		this.observerMap.clear();
	}
	
}//END